package com.zzh.camera2.manager

import android.content.Context
import android.content.res.Configuration
import android.graphics.*
import android.hardware.Camera
import android.hardware.Camera.CameraInfo
import android.hardware.Camera.PreviewCallback
import android.opengl.GLES11Ext
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
import android.util.Log
import android.view.Surface
import android.view.TextureView.SurfaceTextureListener
import android.view.WindowManager
import androidx.annotation.RequiresApi
import com.zzh.camera2.model.CameraParameter
import com.zzh.camera2.model.Const
import com.zzh.camera2.ui.AutoFitTextureView
import com.zzh.camera2.utils.ImageUtils
import com.zzh.camera2.utils.JavaUtils
import java.io.IOException


class CameraApi1Manager : ICameraManager {
    val TAG: String = CameraApi1Manager::class.java.simpleName
    private var camera12PreviewCallback: ICameraPreviewCallback? = null
    private var isShowCallbackView = CameraParameter.isShowCallbackImage
    private var previewFormat = ImageFormat.NV21

    private var previewWidth: Int = Const.previewWidth
    private var previewHeight: Int = Const.previewHeight
    private var mCameraId = 0
    private var mCamera: Camera? = null
    private var textureView: AutoFitTextureView? = null

    // 用于不使用控件显示预览图像的情形
    private val previewDataArray: ArrayList<ByteArray> = ArrayList<ByteArray>()

    // 数组长度，多个数据缓冲区，防止预览回调画面闪烁
    private val previewDataSize = 2
    var mSurfaceTexture: SurfaceTexture? = null

    // 用于处理回调数据
    private var mProcessImageHandler: Handler? = null
    private var mProcessImageThread: HandlerThread? = null
    private val processPreviewDataMsgId = 99

    override fun setPreviewCallback(camera12PreviewCallback: ICameraPreviewCallback) {
        this.camera12PreviewCallback = camera12PreviewCallback
    }

    // 不带控件方式打开相机，只使用回调数据
    override fun openCamera(
        cameraId: Int,
        previewWidth: Int,
        previewHeight: Int,
        imageFormat: Int
    ) {
        mCameraId = cameraId
        this.previewWidth = previewWidth
        this.previewHeight = previewHeight
        this.previewFormat = imageFormat
        // 此变量必须是类成员变量，否则会导致回调函数只调用几帧就没了
        mSurfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
        openCamera(mSurfaceTexture!!, cameraId)
    }

    /**
     * 带预览控件打开相机
     */
    override fun openCamera(
        textureView: AutoFitTextureView,
        cameraId: Int,
        previewWidth: Int,
        previewHeight: Int, imageFormat: Int
    ) {
        mCameraId = cameraId
        this.textureView = textureView
        this.previewWidth = previewWidth
        this.previewHeight = previewHeight
        this.previewFormat = imageFormat
        if (textureView.isAvailable)
            openCamera(textureView.surfaceTexture!!, cameraId)
        else
            textureView.surfaceTextureListener = surfaceTextureListener
    }

    override fun startPreview() {
        if (mCamera != null) {
            mCamera!!.startPreview()
        }
    }

    override fun stopPreview() {
        if (mCamera != null) {
            mCamera!!.stopPreview()
        }
    }

    override fun releaseCamera() {
        if (mCamera != null) {
            val camera: Camera = mCamera!!
            camera.stopPreview()
            if (textureView == null) {
                camera.setPreviewCallbackWithBuffer(null)
            } else {
                camera.setPreviewCallback(null)
            }
            camera.release()
            mCamera = null
        }
        stopBackgroundThread()
    }

    private val surfaceTextureListener = object : SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(
            surfaceTexture: SurfaceTexture,
            width: Int,
            height: Int
        ) {
            openCamera(surfaceTexture, mCameraId)
        }

        override fun onSurfaceTextureSizeChanged(
            surfaceTexture: SurfaceTexture,
            width: Int,
            height: Int
        ) {
            Log.i(TAG, "onSurfaceTextureSizeChanged")
        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
            Log.i(TAG, "onSurfaceTextureDestroyed")
            return false
        }

        override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {

        }
    }

    private fun openCamera(surfaceTexture: SurfaceTexture, cameraId: Int) {
        if (cameraId < 0 /*|| cameraId > Camera.getNumberOfCameras() - 1*/) {
            Log.w(
                TAG,
                "openCamera failed, cameraId=" + cameraId + ", Camera.getNumberOfCameras()=" + Camera.getNumberOfCameras()
            )
            return
        }
        startBackgroundThread()
        try {
//            Log.i(TAG,"surfaceCreated open camera cameraId=$cameraId start")
            mCamera = Camera.open(cameraId)
            if (textureView != null) {
                mCamera!!.setDisplayOrientation(
                    getCameraDisplayOrientation(
                        textureView!!.context,
                        cameraId
                    )
                )
            }
            mCamera!!.setPreviewTexture(surfaceTexture)

            // set preview format @{
            this.previewFormat = setCameraPreviewFormat(mCamera!!, this.previewFormat)
            // @}

            // 设置fps@{
            val minFps: Int = 30000
            val maxFps: Int = 30000
            setCameraPreviewFpsRange(mCamera!!, minFps, maxFps)
            // @}

            // 设置预览尺寸 @{
            val hasSetPreviewSize = setCameraPreviewSize(mCamera!!, previewWidth, previewHeight)
            if (hasSetPreviewSize != null && hasSetPreviewSize.size > 1) {
                previewWidth = hasSetPreviewSize[0]
                previewHeight = hasSetPreviewSize[1]
            }
            // @}

            // 设置照片尺寸 @{
            setCameraPictureSize(mCamera!!, previewWidth, previewHeight)
            // @}

            // 设置预览回调函数@{
            if (textureView == null) {
                mCamera!!.setPreviewCallbackWithBuffer(mCameraCallbacks)
                Log.i(
                    TAG,
                    "ImageFormat: $previewFormat bits per pixel=" + ImageFormat.getBitsPerPixel(
                        previewFormat
                    )
                )
                // 初始化数组
                for (index in 0 until previewDataSize) {
                    val previewData = if (previewFormat != ImageFormat.YV12) {
                        ByteArray(
                            previewWidth * previewHeight * ImageFormat.getBitsPerPixel(
                                previewFormat
                            ) / 8
                        )
                    } else {
                        val size = ImageUtils.getYV12ImagePixelSize(previewWidth, previewHeight)
                        ByteArray(size)
                    }
                    previewDataArray.add(previewData)
                }
                addAllPreviewCallbackData()
            } else {
                mCamera!!.setPreviewCallback(mCameraCallbacks)
            }
            // @}
            autoRatioTextureView()

            mCamera!!.startPreview()
        } catch (localIOException: IOException) {
            Log.e(
                TAG,
                "surfaceCreated open camera localIOException cameraId=" + cameraId + ", error=" + localIOException.message,
                localIOException
            )
        } catch (e: Exception) {
            Log.e(
                TAG,
                "surfaceCreated open camera cameraId=" + cameraId + ", error=" + e.message,
                e
            )
        }
    }

    /**调整显示预览画面的控件宽度和高度**/
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun autoRatioTextureView() {
        if (this.textureView == null) {
            return
        }
        // We fit the aspect ratio of TextureView to the size of preview we picked.
        val orientation: Int = textureView!!.getResources().getConfiguration().orientation
        Log.i("TAG", "orientation=$orientation")
        // 横竖屏都是宽更短，高更长
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            textureView!!.setAspectRatio(previewHeight, previewWidth)
        } else {
            textureView!!.setAspectRatio(previewHeight, previewWidth)
        }
    }

    // 添加用于填充回调数据的缓冲区
    private fun addAllPreviewCallbackData() {
        for (data in previewDataArray) {
            // 设置 mCamera.addCallbackBuffer(mPreviewData) 后才会回调，旨在每处理完一帧数据回调一次
            mCamera!!.addCallbackBuffer(data)
        }
    }

    // add by zzh
    private val mCameraCallbacks: PreviewCallback = PreviewCallback { data, camera ->
        if (!isShowCallbackView) {
            return@PreviewCallback
        }

        // 使用handler thread去处理，防止阻塞
        if (mProcessImageHandler != null) {
            val msg = Message()
            msg.what = processPreviewDataMsgId
            msg.obj = data
            mProcessImageHandler!!.removeMessages(processPreviewDataMsgId)
            mProcessImageHandler!!.sendMessage(msg)
        }

        if (textureView == null) {
            // 在此处处理当前帧数据，并设置下⼀帧回调
            addAllPreviewCallbackData()
        }
    }

    // add by zzh
    private fun setCameraPreviewFormat(camera: Camera, previewFormat: Int): Int {
        var parameters = camera.parameters
        // 默认预览格式
        val defaultPreviewFormat = parameters.previewFormat

        // 设置的预览图像格式是否是相机支持的
        var isSetFormatSuit = false
        var isSupportYV12Format = false
        var supportedFirstFormat = ImageFormat.NV21
        val pfa = parameters.supportedPreviewFormats
        // 列出所有支持的格式
        for (index in 0 until pfa.size) {
            if (index == 0) {
                supportedFirstFormat = pfa[index]
            }
//          Log.i(TAG, "supported format[" + index + "]=" + pfa[index])
            if (previewFormat == pfa[index]) {
                isSetFormatSuit = true
            }
            if (ImageFormat.YV12 == pfa[index]) {
                isSupportYV12Format = true
            }
        }

        if (!isSetFormatSuit) {
            Log.w(TAG, "The image preview format you set is not supported")
        }
        if (isSetFormatSuit) {
            parameters.previewFormat = previewFormat
        } else if (isSupportYV12Format) {
            parameters.previewFormat = ImageFormat.YV12
        } else {
            parameters.previewFormat = supportedFirstFormat
        }
        // 设置预览格式
        camera.parameters = parameters

        Log.i(
            TAG, "surfaceCreated defaultPreviewFormat=" + defaultPreviewFormat
                    + ", after set preview format=" + camera.parameters.previewFormat + ", camera id=$mCameraId"
        )

        return camera.parameters.previewFormat
    }

    // add by zzh setCameraPreviewFpsRange
    private fun setCameraPreviewFpsRange(camera: Camera, minFps: Int, maxFps: Int) {
        var min: Int = 30000
        var max: Int = 30000
        val parameters = camera.parameters
        val defaultPreviewFps = IntArray(2)
        parameters.getPreviewFpsRange(defaultPreviewFps)
        val supportedFpsRangeArray = parameters.supportedPreviewFpsRange
        var isSupportSetFps = false
        for (index in 0 until supportedFpsRangeArray.size) {
//            Log.i(TAG, "supported fps=" + supportedFpsRangeArray[index][0] + ", 1=" + supportedFpsRangeArray[index][1])
            if (index >= supportedFpsRangeArray.size - 1) {
                min = supportedFpsRangeArray[index][0]
                max = supportedFpsRangeArray[index][1]
            }
            if (supportedFpsRangeArray[index][0] == minFps && supportedFpsRangeArray[index][1] == maxFps) {
                isSupportSetFps = true
            }
        }
        if (isSupportSetFps) {
            min = minFps
            max = maxFps
        }
        parameters.setPreviewFpsRange(min, max)
        mCamera!!.parameters = parameters

        val afterFps = IntArray(2)
        mCamera!!.parameters.getPreviewFpsRange(afterFps)
        val isSupportZoom = parameters.isZoomSupported
        Log.i(
            TAG, "surfaceCreated defaultPreviewFps, min = "
                    + defaultPreviewFps[0] + ", max = " + defaultPreviewFps[1] + ", zoom support=" + isSupportZoom
                    + ", after set fps min=" + afterFps[0] + ", max=" + afterFps[1]
        )
    }

    // add by zzh
    private fun setCameraPreviewSize(camera: Camera, width: Int, height: Int): Array<Int> {
        val parameters = camera.parameters
        val defaultPreviewSize = parameters.previewSize
        // set preview size
        val sizes = camera.parameters.supportedPreviewSizes
        var expected = sizes[sizes.size - 1]
        var gotIt = false
        // print all support preview sizes
//        for (size in sizes) {
//            Log.i(TAG, "all Preview size is w:" + size.width + " h:" + size.height)
//        }
        for (size in sizes) {
//          Log.i(TAG, "Preview size is w:" + size.width + " h:" + size.height)
            if (size.width == width && size.height == height) {
                expected = size
                gotIt = true
                Log.i(TAG, "setCameraPreviewSize width,height is supported")
                break
            }
        }
        var resultWidth = expected.width
        var resultHeight = expected.height
        if (!gotIt) {
            resultWidth = width
            resultHeight = height
            Log.i(TAG, "setCameraPreviewSize width,height is not supported")
        }
        parameters.setPreviewSize(resultWidth, resultHeight)
        camera.parameters = parameters
        Log.i(
            "TAG",
            "setCameraPreviewSize defaultPreviewSize width=" + defaultPreviewSize.width + ", height=" + defaultPreviewSize.height
                    + ", after set preview size width=" + camera.parameters.previewSize.width + ", height=" + camera.parameters.previewSize.height
        )

        return arrayOf(resultWidth, resultHeight)
    }

    // add by zzh
    private fun setCameraPictureSize(camera: Camera, width: Int, height: Int): Array<Int> {
        val parameters = camera.parameters
        val defaultPictureSize = parameters.pictureSize
        // set picture size
        val sizes = camera.parameters.supportedPictureSizes
        var expected = sizes[sizes.size - 1]
        var gotIt = false
        // print all support picture sizes
//        for (size in sizes) {
//            Log.i(TAG, "all picture size is w:" + size.width + " h:" + size.height)
//        }
        for (size in sizes) {
//          Log.i(TAG, "Picture size is w:" + size.width + " h:" + size.height)
            if (size.width == width && size.height == height) {
                expected = size
                gotIt = true
                Log.i(TAG, "setCameraPictureSize width,height is supported")
                break
            }
        }
        var resultWidth = expected.width
        var resultHeight = expected.height
        if (!gotIt) {
            resultWidth = width
            resultHeight = height
            Log.i(TAG, "setCameraPictureSize width,height is not supported")
        }
        parameters.setPictureSize(resultWidth, resultHeight)
//        parameters.pictureFormat = previewFormat
//        parameters.setZoom(0)
//        parameters.setRotation(0)
        camera.parameters = parameters
        Log.i(
            "TAG",
            "setCameraPictureSize defaultPictureSize width=" + defaultPictureSize.width + ", height=" + defaultPictureSize.height
                    + ", after set picture size width=" + camera.parameters.pictureSize.width + ", height=" + camera.parameters.pictureSize.height
        )

        return arrayOf(resultWidth, resultHeight)
    }

    // add by zzh
    private fun getCameraDisplayOrientation(context: Context?, cameraId: Int): Int {
        if (context == null) {
            Log.e(TAG, "setCameraDisplayOrientation failed activity is null")
            return 0
        }
        val info = CameraInfo()
        Camera.getCameraInfo(cameraId, info)
        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val rotation = windowManager.defaultDisplay.rotation
        var degrees = 0
        when (rotation) {
            Surface.ROTATION_0 -> degrees = 0
            Surface.ROTATION_90 -> degrees = 90
            Surface.ROTATION_180 -> degrees = 180
            Surface.ROTATION_270 -> degrees = 270
        }
        var result: Int
        if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360
            result = (360 - result) % 360 // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360
        }
        Log.i(TAG, "setCameraDisplayOrientation result=$result")
        return result
    }

    private fun startBackgroundThread() {
        if (mProcessImageThread == null || mProcessImageHandler == null) {
            mProcessImageThread = HandlerThread("Preview data processor")
            mProcessImageThread!!.start()
            mProcessImageHandler = Handler(mProcessImageThread!!.looper, processPreviewCallback)
        }
    }

    private fun stopBackgroundThread() {
        if (mProcessImageThread != null && mProcessImageThread!!.isAlive()) {
            mProcessImageThread!!.quit()
            mProcessImageHandler!!.removeCallbacksAndMessages(null)
            mProcessImageThread = null
            mProcessImageHandler = null
        }
    }

    private val processPreviewCallback = Handler.Callback {
        try {
            val what = it.what
            val dataRsv = it.obj
            if (what == processPreviewDataMsgId && dataRsv != null && dataRsv is ByteArray) {
                val data = dataRsv as ByteArray
                var byteArray: ByteArray? = dataRsv as ByteArray
                var imageFormat = 0
                // camera api1 一般只支持yv12 nv21两种格式
                when (previewFormat) {
                    ImageFormat.YV12, ImageFormat.YUV_420_888 -> {
                        imageFormat = Const.previewImageFormatNV21
                        byteArray = ImageUtils.yv12ToNV21(data, previewWidth, previewHeight)
                    }
                    ImageFormat.RAW10 -> {
                        imageFormat = Const.previewImageFormatRAW16
                        byteArray =
                            JavaUtils.transformRAW10ToRAW16(data, previewWidth, previewHeight)
                    }
                    ImageFormat.NV21 -> {
                        imageFormat = Const.previewImageFormatNV21
                    }
                }
                if (camera12PreviewCallback != null && data != null) {
                    camera12PreviewCallback!!.onPreviewFrame(byteArray!!, imageFormat, this)
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "processPreviewCallback error=" + e.message);
        }
        true
    }
}